home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 090 / pctj8604.arc / CPUID.ASM < prev    next >
Assembly Source File  |  1986-01-29  |  20KB  |  683 lines

  1.      title     CPUID -- Determine CPU & NDP Type
  2.      page     58,122
  3.      name     CPUID
  4.  
  5. COMMENT|
  6.  
  7. CPUID purports to uniquely identify each Intel CPU & NDP used in IBM
  8. PCs and compatibles.  For more details, see the accompanying .TXT
  9. file.
  10.  
  11. Notes on Program Structure
  12. --------------------------
  13.  
  14.    This program uses four segments, two classes, and one group.  It
  15. demonstrates a useful technique for programmers who generate .COM
  16. programs.  In particular, it shows how to use segment classes to
  17. re-order segments, and how to eliminate the linker's warning message
  18. about the absence of a stack segment.
  19.  
  20.    The correspondence between segments and classes is as follows:
  21.  
  22.      Segment    Class
  23.      -------    -----
  24.      STACK        prog
  25.      DATA        data
  26.      MDATA        data
  27.      CODE        prog
  28.  
  29.    The segments appear in the above order in the program source to
  30. avoid forward references in the CODE segment to labels in the
  31. DATA/MDATA segments.  However, because the STACK segment appears first
  32. in the file, it and all segments in the same class are made contiguous
  33. by the linker.    Thus they precede the DATA/MDATA segments in the
  34. resulting .COM file because the latter are in a different class.  In
  35. this manner, although DATA and MDATA precede CODE in the source file,
  36. their order is swapped in the .COM file.  That way there is no need
  37. for an initial skip over the data areas to get to the CODE segment.
  38. As a side benefit, declaring a STACK segment (as the first segment in
  39. the source) also eliminates the linker's warning about that segment
  40. being missing.    Finally, all segments are declared to be in the same
  41. group so the linker can properly resolve offsets.
  42.  
  43.    Note that if you re-assemble the code for any reason, it is
  44. important to use an assembler later than the IBM version 1.0.  That
  45. version has a number of bugs including an annoying habit of
  46. alphabetizing segment names in the .OBJ file.  Such gratuitous
  47. behavior defeats the above technique as well as exhibits generally bad
  48. manners.  If you use IBM MASM 2.0, be sure to specify /S to order the
  49. segments properly.
  50.  
  51.    If the program reports results at variance with your knowledge of
  52. the system, please contact the author.
  53.  
  54. Environments tested in:
  55.  
  56.            CPU Speed
  57.   System    in MHz          CPU           NDP
  58.   --------------------------------------------------
  59.   IBM PC AT      6       Intel 80286          Intel 80287
  60.   IBM PC AT      9       Intel 80286          Intel 80287
  61.   IBM PC AT      6       Intel 80286          none
  62.   IBM PC AT      8.5       Intel 80286          none
  63.   IBM PC      4.77       Intel 8088          Intel 8087-3
  64.   IBM PC      4.77       Intel 8088*          Intel 8087-3
  65.   IBM PC XT      4.77       Intel 8088          none
  66.   IBM PC XT      4.77       Intel 8088          Intel 8087-3
  67.   COMPAQ      4.77       Intel 8088          none
  68.   COMPAQ      4.77       NEC V20          none
  69.   AT&T PC 6300      8       Intel 8086          Intel 8087-2
  70.   AT&T PC 6300      8       NEC V30          Intel 8087-2
  71.   TANDY 2000      8       Intel 80186          none
  72.  
  73.   * = faulty CPU
  74.  
  75. Program structure:
  76.   Group PGROUP:
  77.   Stack   segment STACK, byte-aligned, stack,  class 'prog'
  78.   Program segment CODE,  byte-aligned, public, class 'prog'
  79.   Data      segment DATA,  byte-aligned, public, class 'data'
  80.   Data      segment MDATA, byte-aligned, public, class 'data'
  81.  
  82. Assembly requirements:
  83.  
  84.   Use MASM 1.25 or later.
  85.   With IBM's MASM 2.0 only, use /S to avoid
  86.     alphabetizing the segment names.
  87.   Use /r option to generate real NDP code.
  88.  
  89.   MASM CPUID/r;           to convert .ASM to .OBJ
  90.   LINK CPUID;              to convert .OBJ to .EXE
  91.   EXE2BIN CPUID CPUID.COM      to convert .EXE to .COM
  92.   ERASE CPUID.EXE          to avoid executing .EXE
  93.  
  94.   Note that the linker doesn't warn about a missing stack segment.
  95.  
  96. Copyright free.
  97.  
  98. Original code by:
  99.  
  100.   Bob Smith           May 1985.
  101.   Qualitas, Inc.
  102.   8314 Thoreau Dr.
  103.   Bethesda, MD    20817
  104.   301-469-8848
  105.  
  106.   Arthur Zachai suggested the technique to distinguish within the 808x
  107.   and 8018x families by exploiting the difference in the length of
  108.   their pre-fetch instruction queues.
  109.  
  110. Modifications by:
  111.  
  112. Who            When        Why
  113. ---------------------------------------------------------------------
  114. Bob Smith        21 Jan 86    Distinguish NEC V20/V30s
  115.                     from Intel 8088/8086s
  116.  
  117. |
  118.  
  119.      subttl  Structures, Records, Equates, & Macros
  120.      page
  121. ARG_STR  struc
  122.  
  123.      dw     ?        ; Caller's BP
  124. ARG_OFF  dw     ?        ; Caller's offset
  125. ARG_SEG  dw     ?        ;       segment
  126. ARG_FLG  dw     ?        ;       flags
  127.  
  128. ARG_STR  ends
  129.  
  130. ; Record to define bits in the CPU's & NDP's flags' registers
  131.  
  132. CPUFLAGS record  R0:1,NT:1,IOPL:2,OF:1,DF:1,IF:1,TF:1,SF:1,ZF:1,R1:1,AF:1,R2:1,PF:1,R3:1,CF:1
  133. NDPFLAGS record  R4:3,IC:1,RC:2,PC:2,IEM:1,R5:1,PM:1,UM:1,OM:1,ZM:1,DM:1,IM:1
  134.  
  135. COMMENT|
  136.  
  137. FLG_PIQL     Pre-fetch instruction queue length, 0 => 4-byte,
  138.                              1 => 6-byte
  139. FLG_08         Intel 808x
  140. FLG_NEC      NEC V20 or V30
  141. FLG_18         Intel 8018x
  142. FLG_28         Intel 8028x
  143.  
  144. FLG_87         Intel 8087
  145. FLG_287      Intel 80287
  146.  
  147. FLG_CERR     Faulty CPU
  148. FLG_NERR     Faulty NDP switch setting
  149.  
  150. |
  151.  
  152. FLG     record  RSVD:9,FLG_NERR:1,FLG_CERR:1,FLG_NDP:2,FLG_CPU:3
  153.  
  154. ; CPU-related flags
  155.  
  156. FLG_PIQL equ     001b shl FLG_CPU
  157. FLG_08     equ     000b shl FLG_CPU
  158. FLG_NEC  equ     010b shl FLG_CPU
  159. FLG_18     equ     100b shl FLG_CPU
  160. FLG_28     equ     110b shl FLG_CPU
  161.  
  162. FLG_8088 equ     FLG_08
  163. FLG_8086 equ     FLG_08 or FLG_PIQL
  164. FLG_V20  equ     FLG_NEC
  165. FLG_V30  equ     FLG_NEC or FLG_PIQL
  166. FLG_80188 equ     FLG_18
  167. FLG_80186 equ     FLG_18 or FLG_PIQL
  168. FLG_80286 equ     FLG_28 or FLG_PIQL
  169.  
  170. ; NDP-related flags
  171.  
  172. ;         00b shl FLG_NDP    Not present
  173. FLG_87     equ     01b shl FLG_NDP
  174. FLG_287  equ     10b shl FLG_NDP
  175.  
  176. BEL     equ     07h
  177. LF     equ     0Ah
  178. CR     equ     0Dh
  179. EOS     equ     '$'
  180.  
  181. POPFF     macro
  182.      local     L1,L2
  183.  
  184.      jmp     short L2    ; Skip over IRET
  185. L1:
  186.      iret            ; Pop the CS & IP pushed below along
  187.                 ; w. the flags, our original purpose
  188. L2:
  189.      push     cs        ; Prepare for IRET by pushing CS
  190.      call     L1        ; Push IP, jump to IRET
  191.  
  192.      endm            ; POPFF macro
  193.  
  194. TAB     macro     TYP
  195.  
  196.      push     bx        ; Save for a moment
  197.      and     bx,mask FLG_&TYP ; Isolate flags
  198.      mov     cl,FLG_&TYP    ; Shift amount
  199.      shr     bx,cl        ; Shift to low-order
  200.      shl     bx,1        ; Times two to index table of words
  201.      mov     dx,TYP&MSG_TAB[bx] ; DS:DX ==> descriptive message
  202.      pop     bx        ; Restore
  203.  
  204.      mov     ah,09h     ; Function code to display string
  205.      int     21h        ; Request DOS service
  206.  
  207.      endm            ; TAB macro
  208.      page
  209. INT_VEC  segment at 0        ; Start INT_VEC segment
  210.  
  211.      dd     ?        ; Pointer to INT 00h
  212. INT01_OFF dw     ?        ; Pointer to INT 01h
  213. INT01_SEG dw     ?
  214.  
  215. INT_VEC  ends            ; End INT_VEC segment
  216.  
  217. PGROUP     group     STACK,CODE,DATA,MDATA
  218.  
  219.      
  220. ; The following segment both positions class 'prog' segments lower in
  221. ; memory than others so the first byte of the resulting .COM file is
  222. ; in the CODE segment, as well as satisfies the LINKer's need to have
  223. ; a stack segment.
  224.  
  225. STACK     segment byte stack 'prog' ; Start STACK segment
  226. STACK     ends            ; End STACK segment
  227.  
  228. I11_REC  record  I11_PRN:2,I11_RSV1:2,I11_COM:3,I11_RSV2:1,I11_DISK:2,I11_VID:2,I11_RSV3:2,I11_NDP:1,I11_IPL:1
  229.  
  230. DATA     segment byte public 'data' ; Start DATA segment
  231.      assume  ds:PGROUP
  232.  
  233. OLDINT01_VEC label dword  ; Save area for original INT 01h handler
  234. OLDINT01_OFF dw  ?
  235. OLDINT01_SEG dw  ?
  236.  
  237. NDP_CW     label     word        ; Save area for NDP control word
  238.      db     ?
  239. NDP_CW_HI db     0        ; High byte of control word
  240.  
  241. NDP_ENV  dw     7 dup (?)    ; Save area for NDP environment
  242.  
  243. DATA     ends            ; End DATA segment
  244.      subttl  Message Data Area
  245.      page
  246. MDATA     segment byte public 'data' ; Start MDATA segment
  247.      assume  ds:PGROUP
  248.  
  249. MSG_START db     'CPUID    -- Version 1.0'
  250.       db     CR,LF,EOS
  251.  
  252. MSG_8088 db     'CPU is an Intel 8088.',CR,LF,EOS
  253. MSG_8086 db     'CPU is an Intel 8086.',CR,LF,EOS
  254. MSG_V20  db     'CPU is an NEC V20.',CR,LF,EOS
  255. MSG_V30  db     'CPU is an NEC V30.',CR,LF,EOS
  256. MSG_80188 db     'CPU is an Intel 80188.',CR,LF,EOS
  257. MSG_80186 db     'CPU is an Intel 80186.',CR,LF,EOS
  258. MSG_UNK  db     'CPU is a maverick -- 80288??',CR,LF,EOS
  259. MSG_80286 db     'CPU is an Intel 80286.',CR,LF,EOS
  260.  
  261. CPUMSG_TAB label word
  262.      dw     PGROUP:MSG_8088    ; 000 = Intel 8088
  263.      dw     PGROUP:MSG_8086    ; 001 = Intel 8086
  264.      dw     PGROUP:MSG_V20     ; 010 = NEC V20
  265.      dw     PGROUP:MSG_V30     ; 011 = NEC V30
  266.      dw     PGROUP:MSG_80188    ; 100 = Intel 80188
  267.      dw     PGROUP:MSG_80186    ; 101 = Intel 80186
  268.      dw     PGROUP:MSG_UNK     ; 110 = ?
  269.      dw     PGROUP:MSG_80286    ; 111 = Intel 80286
  270.  
  271. NDPMSG_TAB label word
  272.      dw     PGROUP:MSG_NDPX    ; 00 = No NDP
  273.      dw     PGROUP:MSG_8087    ; 01 = Intel 8087
  274.      dw     PGROUP:MSG_80287    ; 10 = Intel 80287
  275.  
  276. MSG_NDPX db     'NDP is not present.',CR,LF,EOS
  277. MSG_8087 db     'NDP is an Intel 8087.',CR,LF,EOS
  278. MSG_80287 db     'NDP is an Intel 80287.',CR,LF,EOS
  279.  
  280. CERRMSG_TAB label word
  281.      dw     PGROUP:MSG_CPUOK    ; 0 = CPU healthy
  282.      dw     PGROUP:MSG_CPUBAD    ; 1 = CPU faulty
  283.  
  284. MSG_CPUOK db     'CPU appears to be healthy.',CR,LF,EOS
  285. MSG_CPUBAD label byte
  286. db BEL,'*** CPU incorrectly allows interrupts'
  287. db 'after a change to SS ***',CR,LF
  288. db 'It should be replaced with a more recent' 
  289. db 'version as it could crash the',CR,LF
  290. db 'system at seemingly random times.',CR,LF,EOS
  291.  
  292. NERRMSG_TAB label word
  293.      dw     PGROUP:MSG_NDPSWOK   ; 0 = NDP switch set correctly
  294.      dw     PGROUP:MSG_NDPSWERR  ; 1 = NDP switch set incorrectly
  295.  
  296. MSG_NDPSWOK db     EOS            ; No message
  297. MSG_NDPSWERR label  byte
  298. db '*** Although there is an NDP installed' 
  299. db 'on this system, the corresponding',CR,LF
  300. db 'system board switch is not properly set.  '
  301. db 'To correct this, flip switch 2 of',CR,LF
  302. db 'switch block 1 on the system board.',CR,LF,EOS
  303.  
  304. MDATA     ends            ; End MDATA segment
  305.      subttl  Main Routine
  306.      page
  307. CODE     segment byte public 'prog' ; Start CODE segment
  308.      assume  cs:PGROUP,ds:PGROUP,es:PGROUP
  309.  
  310.      org     100h        ; Skip over PSP
  311. INITIAL  proc     near
  312.  
  313.      mov     dx,offset ds:MSG_START ; Starting message
  314.      mov     ah,09h     ; Function code to display string
  315.      int     21h        ; Request DOS service
  316.  
  317.      call     CPUID        ; Check the CPU's identity
  318.  
  319.      TAB     CPU        ; Display CPU results
  320.      TAB     NDP        ; Display NDP results
  321.      TAB     CERR        ; Display CPU ERR results
  322.      TAB     NERR        ; Display NDP ERR results
  323.  
  324.      ret            ; Return to DOS
  325.  
  326. INITIAL  endp            ; End INITIAL procedure
  327.      subttl  CPUID Procedure
  328.      page
  329. CPUID     proc     near        ; Start CPUID procedure
  330.      assume  cs:PGROUP,ds:PGROUP,es:PGROUP
  331.  
  332. COMMENT|
  333.  
  334. This procedure determines the type of CPU and NDP (if any) in use.
  335.  
  336. The possibilities include:
  337.  
  338. Intel 8086
  339. Intel 8088
  340. NEC   V20
  341. NEC   V30
  342. Intel 80186
  343. Intel 80188
  344. Intel 80286
  345. Intel 8087
  346. Intel 80287
  347.  
  348.    Also checked is whether or not the CPU allows interrupts after
  349. changing the SS segment register.  If the CPU does, it is faulty and
  350. should be replaced.
  351.  
  352.    Further, if an NDP is installed, non-AT machines should have a
  353. system board switch set correspondingly.  Such a discrepancy is
  354. reported upon.
  355.  
  356.    On exit, BX contains flag settings (as defined in FLG record) which
  357. the caller can check.  For example, to test for an Intel 80286, use
  358.  
  359.      and     bx,mask FLAG_CPU
  360.  
  361.      cmp     bx,FLG_80286
  362.      je     ITSA286
  363.  
  364. |
  365.  
  366.      irp     XX,<ax,cx,di,ds,es> ; Save registers
  367.      push     XX
  368.      endm
  369.  
  370. ; Test for 80286 -- this CPU executes PUSH SP by first storing SP on 
  371. ; stack, then decrementing it.  Earlier CPUs decrement, THEN store.
  372.  
  373.      mov     bx,FLG_28    ; Assume it's a 286
  374.  
  375.      push     sp        ; Only 286 pushes pre-push SP
  376.      pop     ax        ; Get it back
  377.  
  378.      cmp     ax,sp        ; Check for same
  379.      je     CHECK_PIQL    ; They are, so it's a 286
  380.  
  381. ; Test for 80186/80188 -- 18x and 286 CPUs mask shift/rotate 
  382. ; operations mod 32; earlier CPUs use all 8 bits of CL.
  383.  
  384.      mov     bx,FLG_18    ; Assume it's an 8018x
  385.      mov     cl,32+1    ; 18x masks shift counts mod 32
  386.                 ; Note we can't use just 32 in CL
  387.      mov     al,0FFh    ; Start with all bits set
  388.  
  389.      shl     al,cl        ; Shift one position if 18x
  390.      jnz     CHECK_PIQL    ; Some bits still on, 
  391.                 ; so it's a 18x, check PIQL
  392.  
  393.      mov     bx,FLG_NEC    ; Assume it's an NEC V-series CPU
  394.      call     CHECK_NEC    ; See if it's an NEC chip
  395.      jcxz     CHECK_PIQL    ; Good guess, check PIQL
  396.  
  397.      mov     bx,FLG_08    ; It's an 808x
  398.      subttl  Check Length Of Pre-fetch Instruction Queue
  399.      page
  400. COMMENT|
  401.  
  402. Check the length of the pre-fetch instruction queue (PIQ).
  403.  
  404. xxxx6 CPUs have a PIQ length of 6 bytes,
  405. xxxx8 CPUs   "     "     "      4   "
  406.  
  407. Self-modifying code is used to distinguish the two PIQ lengths.
  408.  
  409. |
  410.  
  411. CHECK_PIQL:
  412.      call     PIQL_SUB    ; Handled via subroutine
  413.      jcxz     CHECK_ERR    ; If CX is 0, INC was not executed,
  414.                 ; hence PIQ length is 4
  415.      or     bx,FLG_PIQL    ; PIQ length is 6
  416.      subttl  Check For Allowing Interrupts After POP SS
  417.      page
  418.  
  419. ; Test for faulty chip (allows interrupts after change to SS register)
  420.  
  421. CHECK_ERR:
  422.      xor     ax,ax        ; Prepare to address 
  423.                 ; interrupt vector segment
  424.      mov     ds,ax        ; DS points to segment 0
  425.      assume  ds:INT_VEC    ; Tell the assembler
  426.  
  427.      cli            ; Nobody move while we swap
  428.  
  429.      mov     ax,offset cs:INT01 ; Point to our own handler
  430.      xchg     ax,INT01_OFF    ; Get and swap offset
  431.      mov     OLDINT01_OFF,ax ; Save to restore later
  432.  
  433.      mov     ax,cs        ; Our handler's segment
  434.      xchg     ax,INT01_SEG    ; Get and swap segment
  435.      mov     OLDINT01_SEG,ax ; Save to restore later
  436.  
  437. ; Note we continue with interrupts disabled to avoid 
  438. ; an external interrupt occurring during this test.
  439.  
  440.      mov     cx,1        ; Initialize a register
  441.      push     ss        ; Save SS to store back into itself
  442.  
  443.      pushf            ; Move flags
  444.      pop     ax        ; ...into AX
  445.      or     ax,mask TF    ; Set trap flag
  446.      push     ax        ; Place onto stack
  447.      POPFF            ; ...and then into effect
  448.                 ; Some CPUs effect the trap flag 
  449.                 ;   immediately, some 
  450.                 ;   wait one instruction
  451.                 
  452.      nop            ; Allow interrupt to take effect
  453. POST_NOP:
  454.      pop     ss        ; Change the stack segment register
  455.                 ;   (to itself)
  456.      dec     cx        ; Normal CPUs execute this instruction
  457.                 ;   before recognizing the single-step 
  458.                 ;   interrupt
  459.      hlt            ; We never get here
  460. INT01:
  461.  
  462. ; Note IF=TF=0
  463.  
  464. ; If we're stopped at or before POST_NOP, continue on
  465.  
  466.      push     bp        ; Prepare to address the stack
  467.      mov     bp,sp        ; Hello, Mr. Stack
  468.  
  469.      cmp     [bp].ARG_OFF,offset cs:POST_NOP ; Check offset
  470.      pop     bp        ; Restore
  471.      ja     INT01_DONE    ; We're done
  472.  
  473.      iret            ; Return to caller
  474. INT01_DONE:
  475.  
  476. ; Restore old INT 01h handler
  477.  
  478.      les     ax,OLDINT01_VEC ; ES:AX ==> old INT 01h handler
  479.      assume  es:nothing    ; Tell the assembler
  480.      mov     INT01_OFF,ax    ; Restore offset
  481.      mov     INT01_SEG,es    ; ...and segment
  482.  
  483.      sti            ; Allow interrupts again (IF=1)
  484.  
  485.      add     sp,3*2     ; Strip IP, CS, and Flags from stack
  486.  
  487.      push     cs        ; Setup DS for code below
  488.      pop     ds
  489.      assume  ds:PGROUP    ; Tell the assembler
  490.  
  491.      jcxz     CHECK_NDP    ; If CX is 0, the DEC CX was executed,
  492.                 ; and the CPU is OK
  493.      or     bx,mask FLG_CERR ; It's a faulty chip
  494.      subttl  Check For Numeric Data Processor
  495.      page
  496. COMMENT|
  497.  
  498.    Test for a Numeric Data Processor -- Intel 8087 or 80287.  The
  499. technique used is passive -- it leaves the NDP in the same state in
  500. which it is found.
  501.  
  502. |
  503.  
  504. CHECK_NDP:
  505.      cli            ; Protect FNSTENV
  506.      fnstenv NDP_ENV    ; If NDP present, save 
  507.                 ;    current environment,
  508.                 ;    otherwise, this instruction 
  509.                 ;    is ignored
  510.      mov     cx,50/7    ; Cycle this many times
  511.      loop     $        ; Wait for result to be stored
  512.      sti            ; Allow interrupts
  513.  
  514.      fninit         ; Initialize processor to known state
  515.      jmp     short $+2    ; Wait for initialization
  516.  
  517.      fnstcw  NDP_CW     ; Save control word
  518.      jmp     short $+2    ; Wait for result to be stored
  519.      jmp     short $+2
  520.  
  521.      cmp     NDP_CW_HI,03h    ; Check for NDP initial control word
  522.      jne     CPUID_EXIT    ; No NDP installed
  523.  
  524.      int     11h        ; Get equipment flags into AX
  525.  
  526.      test     ax,mask I11_NDP ; Check NDP-installed bit
  527.      jnz     CHECK_NDP1    ; It's correctly set
  528.  
  529.      or     bx,mask FLG_NERR ; Mark as in error
  530. CHECK_NDP1:
  531.      and     NDP_CW,not mask IEM ; Enable interrupts 
  532.                      ;    (IEM=0, 8087 only)
  533.      fldcw     NDP_CW     ; Reload control word
  534.      fdisi            ; Disable interrupts (IEM=1) on 8087,
  535.                 ; ignored by 80287
  536.      fstcw     NDP_CW     ; Save control word
  537.      fldenv  NDP_ENV    ; Restore original NDP environment
  538.                 ; No need to wait 
  539.                 ;   for environment to be loaded
  540.  
  541.      test     NDP_CW,mask IEM ; Check Interrupt Enable Mask 
  542.                  ;    (8087 only)
  543.      jnz     CPUID_8087    ; It changed, hence NDP is an 8087
  544.  
  545.      or     bx,FLG_287    ; NDP is an 80287
  546.      jmp     short CPUID_EXIT ; Exit with flags in BX
  547. CPUID_8087:
  548.      or     bx,FLG_87    ; NDP is an 8087
  549. CPUID_EXIT:
  550.      irp     XX,<es,ds,di,cx,ax> ; Restore registers
  551.      pop     XX
  552.      endm
  553.      assume  ds:nothing,es:nothing
  554.  
  555.      ret            ; Return to caller
  556.  
  557. CPUID     endp            ; End CPUID procedure
  558.      subttl  Check For NEC V20/V30
  559.      page
  560. CHECK_NEC proc     near
  561.  
  562. COMMENT|
  563.  
  564.    The NEC V20/V30 CPUs are very compatible with the Intel 8088/8086.
  565. The only point of "incompatiblity" is that they do not contain a bug
  566. found in the Intel CPUs.  Specifically, the NEC CPUs correctly restart
  567. an interrupted multi-prefix string instruction at the start of the
  568. instruction.  The Intel CPUs incorrectly restart it in the middle of
  569. the instruction.  This routine tests for that situation by executing
  570. such an instruction for a sufficiently long period of time for a timer
  571. interrupt to occur.  If at the end of the instruction, CX is zero,
  572. it must be an NEC CPU; if not, it's an Intel CPU.
  573.  
  574.    Note that we're counting on the timer interrupt to do its thing
  575. every 18.2 times per second.
  576.  
  577.    Here's a worst case analysis:  An Intel 8088/8086 executes 65535
  578. iterations of LODSB ES:[SI] in 2+9+13*65535 = 851,966 clock ticks.  If
  579. the Intel 8088/8086 is running at 10 MHz, each clock tick is 100
  580. nanoseconds, hence the entire operation takes 85 milliseconds.    If the
  581. timer is running at normal speed, it interrupts the CPU every 55
  582. millseconds and so should interrupt the repeated string instruction at
  583. least once.
  584.  
  585. |
  586.  
  587.      mov     cx,0FFFFh    ; Move a lot of data
  588.      sti            ; Ensure timer enabled
  589.  
  590. ; Execute multi-prefix instruction.  Note that the value of ES as
  591. ; well as the direction flag setting is irrelevant.
  592.  
  593.      push     ax        ; Save registers
  594.      push     si
  595.      rep lods     byte ptr es:[si]
  596.      pop     si        ; Restore
  597.      pop     ax
  598.  
  599. ; On exit, if CX is zero, it's an NEC CPU, otherwise it's an Intel CPU
  600.  
  601.      ret            ; Return to caller
  602.  
  603. CHECK_NEC endp
  604.      subttl  Pre-fetch Instruction Queue Subroutine
  605.      page
  606. PIQL_SUB proc     near
  607.  
  608. COMMENT|
  609.  
  610.    This subroutine attempts to discern the length of the CPU's
  611. pre-fetch instruction queue (PIQ).
  612.  
  613.    The technique used is to first ensure that the PIQ is full, then
  614. change an instruction which should be in a six-byte PIQ but not in a
  615. four-byte PIQ.    Subsequently, if the original instruction is executed,
  616. the PIQ is six bytes long; if the new instruction is executed, the PIQ
  617. length is four.
  618.  
  619.    We ensure the PIQ is full by executing an instruction which takes
  620. long enough so that the Bus Interface Unit (BIU) can fill the PIQ
  621. while the instruction is executing.
  622.  
  623.    Specifically, for all but the last STOSB, we're simply marking time
  624. waiting for the BIU to fill the PIQ.  The last STOSB actually changes
  625. the instruction.  By that time, the original instruction should be in
  626. a six-byte PIQ but not a four-byte PIQ.
  627.  
  628. |
  629.  
  630.      assume  cs:PGROUP,es:PGROUP
  631.  
  632. @REP     equ     3        ; Repeat the store this many times
  633.  
  634.      std            ; Store backwards
  635.      mov     di,offset es:LAB_INC+@REP-1 ; Change the instructions
  636.                          ;   at ES:DI
  637.                              ; and preceding
  638.      mov     al,ds:LAB_STI    ; Change to a STI
  639.      mov     cx,@REP    ; Give the BIU time 
  640.                 ;    to pre-fetch instructions
  641.      cli            ; Ensure interrupts are disabled,
  642.                 ;    otherwise a timer tick 
  643.                 ;    could change the PIQ filling
  644.      rep stosb        ; Change the instruction
  645.             ; During execution of this instruction the BIU
  646.             ; is refilling the PIQ.  The current 
  647.             ; instruction is no longer in the PIQ.
  648.             ; Note at end, CX is 0
  649.  
  650. ; The PIQ begins filling here
  651.  
  652.      cld            ; Restore direction flag
  653.      nop            ; PIQ fillers
  654.      nop
  655.      nop
  656.  
  657. ; The following instruction is beyond a four-byte-PIQ CPU's reach,
  658. ; but within that of a six-byte-PIQ CPU.
  659.  
  660. LAB_INC  label     byte
  661.      inc     cx        ; Executed only if PIQ length is 6
  662.  
  663. LAB_STI  label     byte
  664.      rept     @REP-1
  665.      sti            ;; Restore interrupts
  666.      endm
  667.  
  668.      ret            ; Return to caller
  669.  
  670.      assume  ds:nothing,es:nothing
  671.  
  672. PIQL_SUB endp            ; End PIQL_SUB procedure
  673.  
  674. CODE     ends            ; End CODE segment
  675.  
  676.      if1
  677. %OUT Pass 1 complete
  678.      else
  679. %OUT Pass 2 complete
  680.      endif
  681.  
  682.      end     INITIAL    ; End CPUID module
  683.